package com.game.game.service.server;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.game.common.Transmitter;
import com.game.common.config.BaseServerConfig;
import com.game.common.constants.GlobalConstants;
import com.game.common.constants.ServerType;
import com.game.core.net.common.RemoteNode;
import com.game.core.service.PublicService;
import com.game.message.proto.server.ServerProtoBuf.BattleInfoPROTO;
import com.game.message.proto.server.ServerProtoBuf.SSPushBattleListSYN;
import com.game.message.proto.server.ServerProtoBuf.UpdateConfigPROTO;

public class ServerService extends PublicService {
	private static final long serialVersionUID = -95924175253120279L;
	static private Logger logger = LoggerFactory.getLogger(ServerService.class);
	private Set<String> onlineAccountIDs = new HashSet<String>();
	private Map<Integer, BaseServerConfig> typeServerConfigs = new ConcurrentHashMap<Integer, BaseServerConfig>();
	private Map<String, BaseServerConfig> nameServerConfigs = new ConcurrentHashMap<String, BaseServerConfig>();
	private Map<Integer, RemoteNode> gatewayNodes = new ConcurrentHashMap<Integer, RemoteNode>(); // <serverID, gatewayNodes>
	private Map<Integer, RemoteNode> battleNodes = new ConcurrentHashMap<Integer, RemoteNode>(); // <serverID , battleNode>
	private Map<String, int[]> accountGateway = new ConcurrentHashMap<String, int[]>();
	private List<BattleInfoPROTO> battleList = new ArrayList<BattleInfoPROTO>();
	private List<UpdateConfigPROTO> updateConfigList = new ArrayList<UpdateConfigPROTO>();
	public BaseServerConfig serverConfig;

	public Lock lock = new ReentrantLock();

	public BaseServerConfig getServerConfig() {
		return serverConfig;
	}

	public void setServerConfig(BaseServerConfig config) {
		serverConfig = config;
	}

	public void setAccountGateway(String accountID, int serverID, int sessionID) {
		accountGateway.put(accountID, new int[] { serverID, sessionID });
	}

	public Map<String, int[]> getAccountGateWayMap() {
		return accountGateway;
	}

	public void addOnlineAccountID(String accountID) {
		synchronized (onlineAccountIDs) {
			onlineAccountIDs.add(accountID);
		}
	}

	public boolean isOnline(String accountID) {
		synchronized (onlineAccountIDs) {
			return onlineAccountIDs.contains(accountID);
		}
	}

	public void removeOnlineAccountID(String accountID) {
		synchronized (onlineAccountIDs) {
			onlineAccountIDs.remove(accountID);
		}
	}

	public Set<String> getOnlineAccountIDs() {
		synchronized (onlineAccountIDs) {
			Set<String> temp = new HashSet<>(onlineAccountIDs);
			return temp;
		}
	}

	public int getOnlineAccountCount() {
		synchronized (onlineAccountIDs) {
			return onlineAccountIDs.size();
		}
	}

	public void onServerDisconnect(RemoteNode remoteNode) {
		synchronized (onlineAccountIDs) {
			int serverID = remoteNode.getServerID();
			if (getGatewayNode(serverID) == null)
				return;

			// 如果gateway掉线，删除这个gateway上面的在线玩家
			Iterator<String> iterator = onlineAccountIDs.iterator();
			while (iterator.hasNext()) {
				String accountID = iterator.next();
				int[] gateway = accountGateway.get(accountID);
				if (gateway != null && gateway.length > 0 && gateway[0] == serverID)
					iterator.remove();
			}
		}
	}

	/**
	 * 
	 * accountID 对应的GateWayNode
	 * 
	 * @param accountID
	 * @return GateWayNode
	 * @see [类、类#方法、类#成员]
	 */
	public RemoteNode getAccountNode(String accountID) {
		int[] gateway = accountGateway.get(accountID);
		if (gateway != null)
			return getGatewayNode(gateway[0]);
		return null;
	}

	public Integer getAccountSession(String accountID) {
		int[] gateway = accountGateway.get(accountID);
		if (gateway != null)
			return gateway[1];
		return null;
	}

	public void setGatewayNode(Integer serverID, RemoteNode node) {
		node.setServerID(serverID);
		gatewayNodes.put(serverID, node);
	}

	public RemoteNode getGatewayNode(Integer serverID) {
		return gatewayNodes.get(serverID);
	}

	public void setBattleNode(Integer serverID, RemoteNode node) {
		node.setServerID(serverID);
		battleNodes.put(serverID, node);
	}

	public RemoteNode getRandomBattleNode() {
		// TODO 增加选择BATTLE算法
		try {
			lock.lock();
			int index = (int) (Math.random() * battleNodes.size());
			return (RemoteNode) battleNodes.values().toArray()[index];
		} finally {
			lock.unlock();
		}

	}

	public RemoteNode getBattleNode(Integer serverID) {
		if (serverID == null)
			return null;
		return battleNodes.get(serverID);
	}

	public List<BattleInfoPROTO> getBattleList() {
		return battleList;
	}

	public List<UpdateConfigPROTO> getUpdateConfigList() {
		return updateConfigList;
	}

	public BaseServerConfig getServerConfig(String serverName) {
		return nameServerConfigs.get(serverName);
	}

	public BaseServerConfig getServerConfig(int serverType) {
		return typeServerConfigs.get(serverType);
	}

	public void updateServerConfig() {
		loadAllServerConfig();
		// 给GatewayServer推送新的battle列表
		SSPushBattleListSYN.Builder battleBuilder = SSPushBattleListSYN.newBuilder();
		battleBuilder.addAllBattles(battleList);
		for (RemoteNode remoteNode : gatewayNodes.values())
			Transmitter.getInstance().write(remoteNode, GlobalConstants.DEFAULT_CALLBACK, battleBuilder.build());
	}

	public void addServerConfig(String serverName, BaseServerConfig config) {
		nameServerConfigs.put(serverName, config);
	}

	public void addServerConfig(int serverType, BaseServerConfig config) {
		typeServerConfigs.put(serverType, config);
		if (serverType == ServerType.BATTLE_SERVER) {
			BattleInfoPROTO.Builder builder = BattleInfoPROTO.newBuilder();
			builder.setServerID(config.getServerID());
			builder.setIpForServer(config.getHostName4Server());
			builder.setPortForServer(config.getPort4Server());
			battleList.clear();
			battleList.add(builder.build());
		}
	}

	public int loadAllServerConfig() {
		String ServerName = null;
		int serverID = 0;
		int ServerType = 0;
		String IPForServer = null;
		int PortForServer = 0;
		String IPForClient = null;
		int PortForClient = 0;
		String IPForGMTools = null;
		int PortForGMTools = 0;
		// 加载数据库配置
		InputStream propertiesStream = null;
		propertiesStream = ServerService.class.getResourceAsStream("/baseInfo.properties");
		Properties properties = new Properties();
		try {
			properties.load(propertiesStream);
		} catch (IOException e) {
			logger.error("Failed load base local config from /baseInfo.properties Exception err={}", e.toString());
		}
		try {
			BaseServerConfig gameServerConfig = new BaseServerConfig();
			if (properties.containsKey("ServerName")) {
				ServerName = properties.getProperty("ServerName").trim();
				if (ServerName != null) {
					gameServerConfig.setServerName(ServerName);
				} else {
					throw new Exception("The game server name can't be null in the baseInfo.properties");
				}
			}

			if (properties.containsKey("ServerID")) {
				serverID = Integer.parseInt(properties.getProperty("ServerID").trim());
				gameServerConfig.setServerID(serverID);
			}
			if (properties.containsKey("ServerType")) {
				ServerType = Integer.parseInt(properties.getProperty("ServerType").trim());
				gameServerConfig.setServerType(ServerType);
			}
			if (properties.containsKey("IPForServer")) {
				IPForServer = properties.getProperty("IPForServer").trim();
				gameServerConfig.setHostName4Server(IPForServer);
			}
			if (properties.containsKey("PortForServer")) {
				PortForServer = Integer.parseInt(properties.getProperty("PortForServer").trim());
				gameServerConfig.setPort4Server(PortForServer);
			}
			if (properties.containsKey("IPForClient")) {
				IPForClient = properties.getProperty("IPForClient").trim();
				gameServerConfig.setHostName4Client(IPForClient);
			}
			if (properties.containsKey("PortForClient")) {
				PortForClient = Integer.parseInt(properties.getProperty("PortForClient").trim());
				gameServerConfig.setPort4Client(PortForClient);
			}
			if (properties.containsKey("IPForGMTools")) {
				IPForGMTools = properties.getProperty("IPForGMTools").trim();
				gameServerConfig.setHostName4GMTools(IPForGMTools);
			}
			if (properties.containsKey("PortForGMTools")) {
				PortForGMTools = Integer.parseInt(properties.getProperty("PortForGMTools").trim());
				gameServerConfig.setPort4GMTools(PortForGMTools);
			}
			addServerConfig(gameServerConfig.getServerType(), gameServerConfig);
			addServerConfig(gameServerConfig.getServerName(), gameServerConfig);

			BaseServerConfig battleServerConfig = new BaseServerConfig();
			if (properties.containsKey("BattleServer.ServerName")) {
				ServerName = properties.getProperty("BattleServer.ServerName").trim();
				if (ServerName != null) {
					battleServerConfig.setServerName(ServerName);
				} else {
					throw new Exception("The battle server name can't be null in the baseInfo.properties");
				}
			}
			if (properties.containsKey("BattleServer.ServerID")) {
				serverID = Integer.parseInt(properties.getProperty("BattleServer.ServerID").trim());
				battleServerConfig.setServerID(serverID);
			}
			if (properties.containsKey("BattleServer.ServerType")) {
				ServerType = Integer.parseInt(properties.getProperty("BattleServer.ServerType").trim());
				battleServerConfig.setServerType(ServerType);
			}
			if (properties.containsKey("BattleServer.IPForServer")) {
				IPForServer = properties.getProperty("BattleServer.IPForServer").trim();
				battleServerConfig.setHostName4Server(IPForServer);
			}
			if (properties.containsKey("BattleServer.PortForServer")) {
				PortForServer = Integer.parseInt(properties.getProperty("BattleServer.PortForServer").trim());
				battleServerConfig.setPort4Server(PortForServer);
			}
			if (properties.containsKey("BattleServer.IPForClient")) {
				IPForClient = properties.getProperty("BattleServer.IPForClient").trim();
				battleServerConfig.setHostName4Client(IPForClient);
			}
			if (properties.containsKey("BattleServer.PortForClient")) {
				PortForClient = Integer.parseInt(properties.getProperty("BattleServer.PortForClient").trim());
				battleServerConfig.setPort4Client(PortForClient);
			}
			if (properties.containsKey("BattleServer.IPForGMTools")) {
				IPForGMTools = properties.getProperty("BattleServer.IPForGMTools").trim();
				battleServerConfig.setHostName4GMTools(IPForGMTools);
			}
			if (properties.containsKey("BattleServer.PortForGMTools")) {
				PortForGMTools = Integer.parseInt(properties.getProperty("BattleServer.PortForGMTools").trim());
				battleServerConfig.setPort4GMTools(PortForGMTools);
			}
			addServerConfig(battleServerConfig.getServerType(), battleServerConfig);
			addServerConfig(battleServerConfig.getServerName(), battleServerConfig);

			BaseServerConfig gatewayServerConfig = new BaseServerConfig();
			if (properties.containsKey("GatewayServer.ServerName")) {
				ServerName = properties.getProperty("GatewayServer.ServerName").trim();
				if (ServerName != null) {
					gatewayServerConfig.setServerName(ServerName);
				} else {
					throw new Exception("The gateway server name can't be null in the baseInfo.properties");
				}
			}
			if (properties.containsKey("GatewayServer.ServerID")) {
				serverID = Integer.parseInt(properties.getProperty("GatewayServer.ServerID").trim());
				gatewayServerConfig.setServerID(serverID);
			}
			if (properties.containsKey("GatewayServer.ServerType")) {
				ServerType = Integer.parseInt(properties.getProperty("GatewayServer.ServerType").trim());
				gatewayServerConfig.setServerType(ServerType);
			}
			if (properties.containsKey("GatewayServer.IPForServer")) {
				IPForServer = properties.getProperty("GatewayServer.IPForServer").trim();
				gatewayServerConfig.setHostName4Server(IPForServer);
			}
			if (properties.containsKey("GatewayServer.PortForServer")) {
				PortForServer = Integer.parseInt(properties.getProperty("GatewayServer.PortForServer").trim());
				gatewayServerConfig.setPort4Server(PortForServer);
			}
			if (properties.containsKey("GatewayServer.IPForClient")) {
				IPForClient = properties.getProperty("GatewayServer.IPForClient").trim();
				gatewayServerConfig.setHostName4Client(IPForClient);
			}
			if (properties.containsKey("GatewayServer.PortForClient")) {
				PortForClient = Integer.parseInt(properties.getProperty("GatewayServer.PortForClient").trim());
				gatewayServerConfig.setPort4Client(PortForClient);
			}
			if (properties.containsKey("GatewayServer.IPForGMTools")) {
				IPForGMTools = properties.getProperty("GatewayServer.IPForGMTools").trim();
				gatewayServerConfig.setHostName4GMTools(IPForGMTools);
			}
			if (properties.containsKey("GatewayServer.PortForGMTools")) {
				PortForGMTools = Integer.parseInt(properties.getProperty("GatewayServer.PortForGMTools").trim());
				gatewayServerConfig.setPort4GMTools(PortForGMTools);
			}
			addServerConfig(gatewayServerConfig.getServerType(), gatewayServerConfig);
			addServerConfig(gatewayServerConfig.getServerName(), gatewayServerConfig);

			logger.info("Success to load all server config!");
		} catch (Exception e) {
			logger.error("Illegal content in the baseInfo.properties Exception err={}", e.toString());
		}
		return 0;
	}

	public final Map<Integer, RemoteNode> getBattleNodes() {
		return Collections.unmodifiableMap(battleNodes);
	}
}
